package cn.lili.modules.wallet.serviceimpl;


import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.properties.RocketmqCustomProperties;
import cn.lili.common.security.AuthUser;
import cn.lili.common.security.context.UserContext;
import cn.lili.common.utils.CurrencyUtil;
import cn.lili.common.utils.SnowFlake;
import cn.lili.common.utils.StringUtils;
import cn.lili.modules.member.entity.dos.Member;
import cn.lili.modules.member.service.MemberService;
import cn.lili.modules.payment.entity.enums.PaymentMethodEnum;
import cn.lili.modules.payment.kit.CashierSupport;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.WithdrawalSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.system.service.SettingService;
import cn.lili.modules.wallet.entity.dos.MemberWallet;
import cn.lili.modules.wallet.entity.dos.MemberWithdrawApply;
import cn.lili.modules.wallet.entity.dos.WalletLog;
import cn.lili.modules.wallet.entity.dto.MemberWalletUpdateDTO;
import cn.lili.modules.wallet.entity.dto.MemberWithdrawalMessage;
import cn.lili.modules.wallet.entity.dto.TransferResultDTO;
import cn.lili.modules.wallet.entity.enums.DepositServiceTypeEnum;
import cn.lili.modules.wallet.entity.enums.WithdrawStatusEnum;
import cn.lili.modules.wallet.entity.vo.MemberWalletVO;
import cn.lili.modules.wallet.mapper.MemberWalletMapper;
import cn.lili.modules.wallet.service.MemberWalletService;
import cn.lili.modules.wallet.service.MemberWithdrawApplyService;
import cn.lili.modules.wallet.service.WalletLogService;
import cn.lili.rocketmq.RocketmqSendCallbackBuilder;
import cn.lili.rocketmq.tags.MemberTagsEnum;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.Gson;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;


/**
 * 会员余额业务层实现
 *
 * @author pikachu
 * @since 2020-02-25 14:10:16
 */
@Service
public class MemberWalletServiceImpl extends ServiceImpl<MemberWalletMapper, MemberWallet> implements MemberWalletService {

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    @Autowired
    private RocketmqCustomProperties rocketmqCustomProperties;

    /**
     * 预存款日志
     */
    @Autowired
    private WalletLogService walletLogService;
    /**
     * 设置
     */
    @Autowired
    private SettingService settingService;
    /**
     * 会员
     */
    @Autowired
    private MemberService memberService;
    /**
     * 会员提现申请
     */
    @Autowired
    private MemberWithdrawApplyService memberWithdrawApplyService;
    @Autowired
    private CashierSupport cashierSupport;

    @Override
    public MemberWalletVO getMemberWallet(String memberId) {
        //构建查询条件
        QueryWrapper<MemberWallet> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("member_id", memberId);
        //执行查询
        MemberWallet memberWallet = this.getOne(queryWrapper, false);
        //如果没有钱包，则创建钱包
        if (memberWallet == null) {
            memberWallet = this.save(memberId, memberService.getById(memberId).getUsername());
        }
        //返回查询数据
        return new MemberWalletVO(memberWallet.getMemberWallet(), memberWallet.getMemberFrozenWallet());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean increaseWithdrawal(MemberWalletUpdateDTO memberWalletUpdateDTO) {
        //检测会员预存款讯息是否存在，如果不存在则新建
        MemberWallet memberWallet = this.checkMemberWallet(memberWalletUpdateDTO.getMemberId());
        //余额变动
        memberWallet.setMemberWallet(CurrencyUtil.add(memberWallet.getMemberWallet(), memberWalletUpdateDTO.getMoney()));
        memberWallet.setMemberFrozenWallet(CurrencyUtil.sub(memberWallet.getMemberFrozenWallet(), memberWalletUpdateDTO.getMoney()));
        this.updateById(memberWallet);
        //新增预存款日志
        WalletLog walletLog = new WalletLog(memberWallet.getMemberName(), memberWalletUpdateDTO);
        walletLogService.save(walletLog);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean increase(MemberWalletUpdateDTO memberWalletUpdateDTO) {
        //检测会员预存款讯息是否存在，如果不存在则新建
        MemberWallet memberWallet = this.checkMemberWallet(memberWalletUpdateDTO.getMemberId());
        //新增预存款
        memberWallet.setMemberWallet(CurrencyUtil.add(memberWallet.getMemberWallet(), memberWalletUpdateDTO.getMoney()));
        this.baseMapper.updateById(memberWallet);
        //新增预存款日志
        WalletLog walletLog = new WalletLog(memberWallet.getMemberName(), memberWalletUpdateDTO);
        walletLogService.save(walletLog);
        return true;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean reduce(MemberWalletUpdateDTO memberWalletUpdateDTO) {
        //检测会员预存款讯息是否存在，如果不存在则新建
        MemberWallet memberWallet = this.checkMemberWallet(memberWalletUpdateDTO.getMemberId());
        //减少预存款，需要校验 如果不够扣减预存款
        if (0 > CurrencyUtil.sub(memberWallet.getMemberWallet(), memberWalletUpdateDTO.getMoney())) {
            return false;
        }
        memberWallet.setMemberWallet(CurrencyUtil.sub(memberWallet.getMemberWallet(), memberWalletUpdateDTO.getMoney()));
        //保存记录
        this.updateById(memberWallet);
        //新增预存款日志
        WalletLog walletLog = new WalletLog(memberWallet.getMemberName(), memberWalletUpdateDTO, true);
        walletLogService.save(walletLog);
        return true;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean reduceWithdrawal(MemberWalletUpdateDTO memberWalletUpdateDTO) {
        //检测会员预存款讯息是否存在，如果不存在则新建
        MemberWallet memberWallet = this.checkMemberWallet(memberWalletUpdateDTO.getMemberId());
        //减少预存款，需要校验 如果不够扣减预存款
        if (0 > CurrencyUtil.sub(memberWallet.getMemberWallet(), memberWalletUpdateDTO.getMoney())) {
            throw new ServiceException(ResultCode.WALLET_WITHDRAWAL_INSUFFICIENT);
        }
        memberWallet.setMemberWallet(CurrencyUtil.sub(memberWallet.getMemberWallet(), memberWalletUpdateDTO.getMoney()));
        memberWallet.setMemberFrozenWallet(CurrencyUtil.add(memberWallet.getMemberFrozenWallet(), memberWalletUpdateDTO.getMoney()));
        //修改余额
        this.updateById(memberWallet);
        //新增预存款日志
        WalletLog walletLog = new WalletLog(memberWallet.getMemberName(), memberWalletUpdateDTO, true);
        walletLogService.save(walletLog);
        return true;

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean reduceFrozen(MemberWalletUpdateDTO memberWalletUpdateDTO) {
        //检测会员预存款讯息是否存在，如果不存在则新建
        MemberWallet memberWallet = this.checkMemberWallet(memberWalletUpdateDTO.getMemberId());
        //校验此金额是否超过冻结金额
        if (0 > CurrencyUtil.sub(memberWallet.getMemberFrozenWallet(), memberWalletUpdateDTO.getMoney())) {
            throw new ServiceException(ResultCode.WALLET_WITHDRAWAL_FROZEN_AMOUNT_INSUFFICIENT);
        }
        memberWallet.setMemberFrozenWallet(CurrencyUtil.sub(memberWallet.getMemberFrozenWallet(), memberWalletUpdateDTO.getMoney()));
        this.updateById(memberWallet);
        //新增预存款日志
        WalletLog walletLog = new WalletLog(memberWallet.getMemberName(), memberWalletUpdateDTO, true);
        walletLogService.save(walletLog);
        return true;
    }

    /**
     * 检测会员预存款是否存在，如果不存在则新建
     *
     * @param memberId 会员id
     */
    private MemberWallet checkMemberWallet(String memberId) {
        //获取会员预存款信息
        MemberWallet memberWallet = this.getOne(new QueryWrapper<MemberWallet>().eq("member_id", memberId), false);
        //如果会员预存款信息不存在则同步重新建立预存款信息
        if (memberWallet == null) {
            Member member = memberService.getById(memberId);
            if (member != null) {
                memberWallet = this.save(memberId, member.getUsername());
            } else {
                throw new ServiceException(ResultCode.USER_AUTHORITY_ERROR);
            }
        }
        return memberWallet;
    }

    @Override
    public void setMemberWalletPassword(Member member, String password) {
        //对密码进行加密
        String pwd = new BCryptPasswordEncoder().encode(password);
        //校验会员预存款是否存在
        QueryWrapper<MemberWallet> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("member_id", member.getId());
        MemberWallet memberWallet = this.getOne(queryWrapper);
        //如果 预存款信息不为空 执行设置密码
        if (memberWallet != null) {
            memberWallet.setWalletPassword(pwd);
            this.updateById(memberWallet);
        }
    }


    @Override
    public Boolean checkPassword() {
        //获取当前登录会员
        AuthUser authUser = UserContext.getCurrentUser();
        //构建查询条件
        QueryWrapper<MemberWallet> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("member_id", authUser.getId());
        MemberWallet wallet = this.getOne(queryWrapper);
        return wallet != null && !StringUtils.isEmpty(wallet.getWalletPassword());
    }

    @Override
    public MemberWallet save(String memberId, String memberName) {
        //获取会员预存款信息
        MemberWallet memberWallet = this.getOne(new QueryWrapper<MemberWallet>().eq("member_id", memberId));
        if (memberWallet != null) {
            return memberWallet;
        }
        memberWallet = new MemberWallet();
        memberWallet.setMemberId(memberId);
        memberWallet.setMemberName(memberName);
        memberWallet.setMemberWallet(0D);
        memberWallet.setMemberFrozenWallet(0D);
        this.save(memberWallet);
        return memberWallet;
    }

    /**
     * 提现方法
     * 1、提现申请冻结用户的余额。
     * 2、判断是否需要平台审核。不需要审核则直接调用第三方提现，需要审核则审核通过后调用第三方提现
     *
     * @param price 提现金额
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean applyWithdrawal(Double price, String realName, String connectNumber) {

        if (price == null || price <= 0 || price > 1000000) {
            throw new ServiceException(ResultCode.WALLET_WITHDRAWAL_AMOUNT_ERROR);
        }
        AuthUser authUser = UserContext.getCurrentUser();

        //校验金额是否满足提现，因为是从余额扣减，所以校验的是余额
        MemberWalletVO memberWalletVO = this.getMemberWallet(authUser.getId());
        if (memberWalletVO.getMemberWallet() < price) {
            throw new ServiceException(ResultCode.WALLET_WITHDRAWAL_INSUFFICIENT);
        }
        //获取提现设置
        Setting setting = settingService.get(SettingEnum.WITHDRAWAL_SETTING.name());
        WithdrawalSetting withdrawalSetting = new Gson().fromJson(setting.getSettingValue(), WithdrawalSetting.class);

        //判断金额是否小于最低提现金额
        if (price < withdrawalSetting.getMinPrice()) {
            throw new ServiceException(ResultCode.WALLET_APPLY_MIN_PRICE_ERROR.message());
        }

        //构建审核参数
        MemberWithdrawApply memberWithdrawApply = new MemberWithdrawApply();
        memberWithdrawApply.setMemberId(authUser.getId());
        memberWithdrawApply.setMemberName(authUser.getNickName());
        memberWithdrawApply.setApplyMoney(price);
        memberWithdrawApply.setRealName(realName);
        memberWithdrawApply.setConnectNumber(connectNumber);

        //判断提现是否需要审核
        if (withdrawalSetting.getApply()) {
            memberWithdrawApply.setApplyStatus(WithdrawStatusEnum.APPLY.name());
        } else {
            memberWithdrawApply.setApplyStatus(WithdrawStatusEnum.VIA_AUDITING.name());
        }

        memberWithdrawApply.setSn("W" + SnowFlake.getId());

        //添加提现申请记录
        memberWithdrawApplyService.save(memberWithdrawApply);

        //扣减余额到冻结金额
        this.reduceWithdrawal(new MemberWalletUpdateDTO(price, authUser.getId(), "提现金额已冻结", DepositServiceTypeEnum.WALLET_WITHDRAWAL.name()));

        //发送余额提现申请消息
        MemberWithdrawalMessage memberWithdrawalMessage = new MemberWithdrawalMessage();
        memberWithdrawalMessage.setMemberWithdrawApplyId(memberWithdrawApply.getId());
        memberWithdrawalMessage.setStatus(memberWithdrawApply.getApplyStatus());
        memberWithdrawalMessage.setMemberId(authUser.getId());
        memberWithdrawalMessage.setPrice(price);
        String destination = rocketmqCustomProperties.getMemberTopic() + ":" + MemberTagsEnum.MEMBER_WITHDRAWAL.name();
        rocketMQTemplate.asyncSend(destination, memberWithdrawalMessage, RocketmqSendCallbackBuilder.commonCallback());

        return true;
    }

    @Override
    public void withdrawal(String withdrawApplyId) {
        MemberWithdrawApply memberWithdrawApply = memberWithdrawApplyService.getById(withdrawApplyId);
        memberWithdrawApply.setInspectTime(new Date());
        //获取提现设置
        Setting setting = settingService.get(SettingEnum.WITHDRAWAL_SETTING.name());
        WithdrawalSetting withdrawalSetting = new Gson().fromJson(setting.getSettingValue(), WithdrawalSetting.class);

        //调用提现方法
        TransferResultDTO transferResultDTO = "WECHAT".equals(withdrawalSetting.getType()) ?
                cashierSupport.transfer(PaymentMethodEnum.WECHAT, memberWithdrawApply) : cashierSupport.transfer(PaymentMethodEnum.ALIPAY,
                memberWithdrawApply);

        //成功则扣减冻结金额
        //失败则恢复冻结金额

        if (transferResultDTO.getResult()) {
            memberWithdrawApply.setApplyStatus(WithdrawStatusEnum.SUCCESS.name());
        } else {
            memberWithdrawApply.setApplyStatus(WithdrawStatusEnum.ERROR.name());
            memberWithdrawApply.setErrorMessage(transferResultDTO.getResponse());
        }
        //修改提现申请
        this.memberWithdrawApplyService.updateById(memberWithdrawApply);

        //发送余额提现申请消息
        MemberWithdrawalMessage memberWithdrawalMessage = new MemberWithdrawalMessage();
        memberWithdrawalMessage.setMemberWithdrawApplyId(memberWithdrawApply.getId());
        memberWithdrawalMessage.setStatus(memberWithdrawApply.getApplyStatus());
        memberWithdrawalMessage.setMemberId(memberWithdrawApply.getMemberId());
        memberWithdrawalMessage.setPrice(memberWithdrawApply.getApplyMoney());
        memberWithdrawalMessage.setStatus(memberWithdrawApply.getApplyStatus());

        String destination = rocketmqCustomProperties.getMemberTopic() + ":" + MemberTagsEnum.MEMBER_WITHDRAWAL.name();
        rocketMQTemplate.asyncSend(destination, memberWithdrawalMessage, RocketmqSendCallbackBuilder.commonCallback());
    }

}